package com.chuangke.flow.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.twelvemonkeys.lang.StringUtil;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.chuangke.admin.data.builder.DeptDataBuilder;
import com.chuangke.admin.data.builder.UserDataBuilder;
import com.chuangke.admin.entity.SysUser;
import com.chuangke.admin.service.SysUserService;
import com.chuangke.common.data.builder.DataBuildConfig;
import com.chuangke.common.data.builder.DataBuilder;
import com.chuangke.common.db.page.CkPageQuery;
import com.chuangke.common.enums.OrderStatusEnum;
import com.chuangke.common.exception.ChuangkeException;
import com.chuangke.flow.dao.FlowHiFlowDao;
import com.chuangke.flow.dao.FlowHiNodeDao;
import com.chuangke.flow.dao.FlowHiTaskDao;
import com.chuangke.flow.dao.FlowHiTaskLogDao;
import com.chuangke.flow.dao.FlowRuFlowDao;
import com.chuangke.flow.dao.FlowRuTaskDao;
import com.chuangke.flow.dao.mapper.FlowHiFlowMapper;
import com.chuangke.flow.dao.mapper.FlowRuFlowMapper;
import com.chuangke.flow.entity.FlowHiFlow;
import com.chuangke.flow.entity.FlowHiNode;
import com.chuangke.flow.entity.FlowHiTask;
import com.chuangke.flow.entity.FlowHiTaskLog;
import com.chuangke.flow.entity.FlowRuFlow;
import com.chuangke.flow.entity.FlowRuNode;
import com.chuangke.flow.entity.FlowRuTask;
import com.chuangke.flow.enums.FlowNodeStatusEnum;
import com.chuangke.flow.service.FlowQueryService;

import cn.hutool.core.util.StrUtil;

@Service
public class FlowQueryServiceImpl implements FlowQueryService {
	
	@Autowired
	private FlowHiFlowDao flowHiFlowDao;
	
	@Autowired
	private FlowRuFlowMapper flowRuFlowMapper;
	@Autowired
	private FlowHiFlowMapper flowHiFlowMapper;
	
	@Autowired
	private FlowRuFlowDao flowRuFlowDao;
	@Autowired
	private FlowRuTaskDao flowRuTaskDao;
	@Autowired
	private FlowHiTaskDao flowHiTaskDao;
	@Autowired
	private FlowHiTaskLogDao flowHiTaskLogDao;
	@Autowired
	private FlowHiNodeDao flowHiNodeDao;
	@Autowired
	private SysUserService sysUserService;
	
	private void wrapper(List<?> list) {
		DataBuilder.of(list).appendBuilder(new UserDataBuilder(), new DataBuildConfig("createdBy", "createdBy"),
				new DataBuildConfig("updatedBy", "updatedBy"),new DataBuildConfig("orderCreatedBy", "orderCreatedByName")
				).appendBuilder(new DeptDataBuilder(), new DataBuildConfig("orderDeptId", "orderDeptName")
						).build();
	}

	@Override
	public IPage<FlowHiFlow> findMyCreatedTask(CkPageQuery pageQuery) {
		String userId = (String) pageQuery.get("userId");
		IPage<FlowHiFlow> result = flowHiFlowDao.page(pageQuery.getPage(), new QueryWrapper<FlowHiFlow>().lambda()
				.eq(FlowHiFlow::getOrderCreatedBy, userId).orderByDesc(FlowHiFlow::getOrderCreatedAt)) ;
		wrapper(result.getRecords()) ;
		return result;
	}
	@Override
	public IPage<FlowHiFlow> findMyCreatedTaskForName(CkPageQuery pageQuery) {
//		String userId = (String) pageQuery.get("userId");
//		IPage<FlowHiFlow> result = flowHiFlowDao.page(pageQuery.getPage(), new QueryWrapper<FlowHiFlow>().lambda()
//				.eq(FlowHiFlow::getOrderCreatedBy, userId).orderByDesc(FlowHiFlow::getOrderCreatedAt)) ;
//		wrapper(result.getRecords()) ;
//		return result;
		String userId = (String) pageQuery.get("userId");
		IPage<FlowHiFlow> result = flowHiFlowMapper.selectFlowAndMyTaskName(pageQuery.getPage(), userId);
		List<FlowHiFlow> list = result.getRecords();
		list = list.stream().map(item->{
			if (!StringUtil.isEmpty(item.getTaskRemark())) {
				item.setTaskName(item.getTaskName() + "(" + item.getTaskRemark() + ")");
			}
			return item;
		}).collect(Collectors.toList());
		result.setRecords(list);
		wrapper(result.getRecords()) ;
		return result;
	}
	
	@Override
	public long findMyCreatedTaskCount(String userId) {
		return flowHiFlowDao.count( new QueryWrapper<FlowHiFlow>().lambda()
				.eq(FlowHiFlow::getOrderCreatedBy, userId).orderByDesc(FlowHiFlow::getOrderCreatedAt)) ;
	}

	@Override
	public IPage<FlowRuFlow> findTodoTask(CkPageQuery pageQuery) {
		String userId = (String) pageQuery.get("userId");
		IPage<FlowRuFlow> result = flowRuFlowMapper.selectFlowByStatus(pageQuery.getPage(), userId, FlowNodeStatusEnum.unhandle.getCode());
		wrapper(result.getRecords()) ;
		return result;
	}
	@Override
	public IPage<FlowRuFlow> findTodoForName(CkPageQuery pageQuery) {
		String userId = (String) pageQuery.get("userId");
		IPage<FlowRuFlow> result = flowRuFlowMapper.selectFlowAndTaskNameByStatus(pageQuery.getPage(), userId, FlowNodeStatusEnum.unhandle.getCode());
		List<FlowRuFlow> list = result.getRecords();
		list = list.stream().map(item->{
			if (!StringUtil.isEmpty(item.getTaskRemark())) {
				item.setTaskName(item.getTaskName() + "(" + item.getTaskRemark() + ")");
			}
			return item;
		}).collect(Collectors.toList());
		result.setRecords(list);
		wrapper(result.getRecords()) ;
		return result;
	}

	@Override
	public List<FlowRuTask> listByUserAndOrderIds(String userId,List<String> orderIds) {
		QueryWrapper<FlowRuTask> qw = new QueryWrapper<>();
		qw.lambda().eq(FlowRuTask::getUserId,userId);
		qw.lambda().ne(FlowRuTask::getStatus,FlowNodeStatusEnum.unstart.getCode());
		qw.lambda().in(FlowRuTask::getOrderId,orderIds);
		return flowRuTaskDao.list(qw);
	}
	
	@Override
	public IPage<FlowRuFlow> findTodoTask2(CkPageQuery pageQuery) {
		return flowRuFlowDao.findTodoTask(pageQuery);
	}
	
	@Override
	public int findTodoTaskCount(String userId) {
		return flowRuFlowMapper.selectFlowCountByStatus(userId, FlowNodeStatusEnum.unhandle.getCode());
	}
	
	@Override
	public long findTodoTaskCount2(HashMap<String, Object> params) {
		return flowRuFlowDao.findTodoTaskCount(params) ;
	}

	@Override
	public IPage<FlowHiFlow> findDoneTask(CkPageQuery pageQuery) {
		String userId = (String) pageQuery.get("userId");
		IPage<FlowHiFlow> result =  flowHiFlowMapper.selectDone(pageQuery.getPage(), userId, FlowNodeStatusEnum.agree.getCode(),FlowNodeStatusEnum.reject.getCode());
		wrapper(result.getRecords()) ;
		return result;
	}
	@Override
	public IPage<FlowHiFlow> findDoneTaskWithName(CkPageQuery pageQuery) {
//		String userId = (String) pageQuery.get("userId");
//		IPage<FlowHiFlow> result =  flowHiFlowMapper.selectDone(pageQuery.getPage(), userId, FlowNodeStatusEnum.agree.getCode(),FlowNodeStatusEnum.reject.getCode());
//		wrapper(result.getRecords()) ;
//		return result;
		String userId = (String) pageQuery.get("userId");
		IPage<FlowHiFlow> result = flowHiFlowMapper.selectDoneWithName(pageQuery.getPage(), userId, FlowNodeStatusEnum.agree.getCode(),FlowNodeStatusEnum.reject.getCode());
		List<FlowHiFlow> list = result.getRecords();
		list = list.stream().map(item->{
			if (!StringUtil.isEmpty(item.getTaskRemark())) {
				item.setTaskName(item.getTaskName() + "(" + item.getTaskRemark() + ")");
			}
			return item;
		}).collect(Collectors.toList());
		result.setRecords(list);
		wrapper(result.getRecords()) ;
		return result;
	}
	
	@Override
	public int findDoneTaskCount(String userId) {
		return flowHiFlowMapper.selectDoneCount( userId, FlowNodeStatusEnum.agree.getCode(),FlowNodeStatusEnum.reject.getCode());
	}

	@Override
	public IPage<FlowHiFlow> findDownAndOverTask(CkPageQuery pageQuery) {
		String userId = (String) pageQuery.get("userId");
		IPage<FlowHiFlow> result =  flowHiFlowMapper.selectDownAndOver(pageQuery.getPage(), userId, OrderStatusEnum.COMPLETE.getCode(),FlowNodeStatusEnum.agree.getCode(),FlowNodeStatusEnum.reject.getCode());
		wrapper(result.getRecords()) ;
		return result;
	}
	@Override
	public IPage<FlowHiFlow> findDownAndOverTaskWithName(CkPageQuery pageQuery) {
		String userId = (String) pageQuery.get("userId");
		IPage<FlowHiFlow> result =  flowHiFlowMapper.selectDownAndOverWithName(pageQuery.getPage(), userId, OrderStatusEnum.COMPLETE.getCode(),FlowNodeStatusEnum.agree.getCode(),FlowNodeStatusEnum.reject.getCode());
		List<FlowHiFlow> list = result.getRecords();
		list = list.stream().map(item->{
			if (!StringUtil.isEmpty(item.getTaskRemark())) {
				item.setTaskName(item.getTaskName() + "(" + item.getTaskRemark() + ")");
			}
			return item;
		}).collect(Collectors.toList());
		result.setRecords(list);
		wrapper(result.getRecords()) ;
		return result;
	}
	
	@Override
	public int findDownAndOverTaskCount(String userId) {
		return flowHiFlowMapper.selectDownAndOverCount(userId, OrderStatusEnum.COMPLETE.getCode(),FlowNodeStatusEnum.agree.getCode(),FlowNodeStatusEnum.reject.getCode());
	}

	@Override
	public IPage<FlowHiFlow> findRejectTask(CkPageQuery pageQuery) {
		String userId = (String) pageQuery.get("userId");
		IPage<FlowHiFlow> result =  flowHiFlowMapper.selectReject(pageQuery.getPage(), userId, FlowNodeStatusEnum.reject.getCode());
		wrapper(result.getRecords()) ;
		return result;
	}
	@Override
	public IPage<FlowHiFlow> findRejectTaskWithName(CkPageQuery pageQuery) {
		String userId = (String) pageQuery.get("userId");
		IPage<FlowHiFlow> result =  flowHiFlowMapper.selectRejectWithName(pageQuery.getPage(), userId, FlowNodeStatusEnum.reject.getCode());
		List<FlowHiFlow> list = result.getRecords();
		list = list.stream().map(item->{
			if (!StringUtil.isEmpty(item.getTaskRemark())) {
				item.setTaskName(item.getTaskName() + "(" + item.getTaskRemark() + ")");
			}
			return item;
		}).collect(Collectors.toList());
		result.setRecords(list);
		wrapper(result.getRecords()) ;
		return result;
	}
	
	@Override
	public int findRejectTaskCount(String userId) {
		return flowHiFlowMapper.selectRejectCount( userId, FlowNodeStatusEnum.reject.getCode());
	}
	
	@Override
	public Map<String, Map<String, Object>> listTask(String businessKey) {
		Map<String, FlowHiNode> nodeMap = getNodeMapByBusinessKey(businessKey);

		List<FlowHiTask> taskList = flowHiTaskDao
				.list(new QueryWrapper<FlowHiTask>().lambda().eq(FlowHiTask::getOrderId, businessKey));

		List<FlowRuTask> currentTaskList = flowRuTaskDao
				.list(new QueryWrapper<FlowRuTask>().lambda().eq(FlowRuTask::getOrderId, businessKey)
						.eq(FlowRuTask::getStatus, FlowNodeStatusEnum.unhandle.getCode()));

		Map<String, List<FlowHiTask>> taskMap1 = CollectionUtils.isEmpty(taskList) ? new HashMap<>()
				: taskList.stream().collect(Collectors.groupingBy(e -> e.getOrderNum() + ""));

		Map<String, List<FlowHiTask>> taskMap = new LinkedHashMap<>();
		taskMap1.entrySet().stream().sorted(Map.Entry.comparingByKey())
				.forEachOrdered(e -> taskMap.put(e.getKey(), e.getValue()));

		taskMap.values().forEach(list -> DataBuilder.of(list)
				.appendBuilder(new UserDataBuilder(), new DataBuildConfig("userId", "userName")).build());

		Map<String, Map<String, Object>> result = new HashMap<>();
		taskMap.keySet().forEach(taskOrder -> {
			Map<String, Object> value;
			try {
				value = new HashMap<>(BeanUtils.describe(nodeMap.get(taskMap.get(taskOrder).get(0).getNodeId())));
			} catch (Exception e) {
				throw new ChuangkeException(e);
			}
			value.put("taskUsers", taskMap.get(taskOrder));
			if (CollectionUtils.isEmpty(currentTaskList)) {
				value.put("taskStatus", "passed");
			} else {
				if (taskOrder.compareTo(currentTaskList.get(0).getOrderNum() + "") < 0) {
					value.put("taskStatus", "finish");
				} else if (taskOrder.compareTo(currentTaskList.get(0).getOrderNum() + "") == 0) {
					value.put("taskStatus", "process");
				} else if (taskOrder.compareTo(currentTaskList.get(0).getOrderNum() + "") > 0) {
					value.put("taskStatus", "wait");
				}
			}
			result.put(taskOrder, value);
		});

		return result;
	}

	@Override
	public List<Map<String, Object>> listApprovalInfo(String businessKey) {
		List<FlowHiTaskLog> taskList = flowHiTaskLogDao
				.list(new QueryWrapper<FlowHiTaskLog>().lambda().eq(FlowHiTaskLog::getOrderId, businessKey)
						.notIn(FlowRuTask::getStatus, Collections.singletonList(FlowNodeStatusEnum.unstart.getCode())));

		if (CollectionUtils.isEmpty(taskList)) {
			return new ArrayList<>();
		}

		DataBuilder.of(taskList).appendBuilder(new UserDataBuilder(), new DataBuildConfig("userId", "userName"))
				.build();
		taskList.forEach(task -> {
			FlowNodeStatusEnum statusEnum = FlowNodeStatusEnum.getEnum(task.getStatus());
			if (statusEnum != null) {
				task.setStatusName(statusEnum.getDesc());
			}
			if (FlowNodeStatusEnum.agree.getCode().equals(task.getStatus()) && StrUtil.isBlank(task.getComments())) {
				task.setComments("同意");
			}
		});

		List<FlowHiTaskLog> currentTaskList = new ArrayList<>();
		taskList = taskList.stream().filter(task -> {
			if (FlowNodeStatusEnum.unhandle.getCode().equals(task.getStatus())) {
				currentTaskList.add(task);
				return false;
			} else {
				return true;
			}
		}).collect(Collectors.toList());

		if (CollectionUtils.isNotEmpty(currentTaskList)) {
			if (currentTaskList.size() == 1) {
				taskList.addAll(currentTaskList);
			} else {
				FlowHiTaskLog currentTask = currentTaskList.get(0);
				for (int i = 1; i < currentTaskList.size(); i++) {
					currentTask.setUserId(currentTask.getUserId() + ";" + currentTaskList.get(i).getUserId());
					currentTask.setUserName(currentTask.getUserName() + ";" + currentTaskList.get(i).getUserName());
				}
				taskList.add(currentTask);
			}
		}

		taskList.sort(Comparator.comparing(FlowHiTaskLog::getStartAt).reversed()
				.thenComparing(Comparator.comparing(FlowHiTaskLog::getOrderNum).reversed())
				);

		Map<String, FlowHiNode> nodeMap = getNodeMapByBusinessKey(businessKey);

		List<Map<String, Object>> result = new ArrayList<>();
		taskList.forEach(task -> {
			try {
                FlowHiNode node = nodeMap.get(task.getNodeId());
                Map<String, Object> value = new HashMap<>(BeanUtils.describe(task));
				value.put("nodeTitle", node.getTitle());
				value.put("nodeContent", node.getContent());
				value.put("createdAt", task.getCreatedAt());
				value.put("updatedAt", task.getUpdatedAt());
				value.put("startAt", task.getStartAt());
				value.put("endAt", task.getEndAt());
				value.put("status", task.getStatus()) ;
				value.put("orderNum", task.getOrderNum()) ;
				result.add(value);
			} catch (Exception e) {
				throw new ChuangkeException(e);
			}
		});

		return result;
	}
	
	private Map<String, FlowHiNode> getNodeMapByBusinessKey(String businessKey) {
		List<FlowHiNode> nodeList = flowHiNodeDao
				.list(new QueryWrapper<FlowHiNode>().lambda().eq(FlowRuNode::getOrderId, businessKey));
		return CollectionUtils.isEmpty(nodeList) ? new HashMap<>()
				: nodeList.stream().collect(Collectors.toMap(FlowHiNode::getId, a -> a));
	}

	@Override
	public List<Map<String, String>> listNextAuditUsers(String orderId, String userId) {
		QueryWrapper<FlowRuTask> qw = new QueryWrapper<>();
		qw.lambda().eq(FlowRuTask::getUserId,userId);
		qw.lambda().eq(FlowRuTask::getStatus,FlowNodeStatusEnum.unhandle.getCode());
		qw.lambda().eq(FlowRuTask::getOrderId,orderId);
		List<FlowRuTask> taskList = flowRuTaskDao.list(qw);
		
		if(CollectionUtils.isEmpty(taskList)) {
			throw new ChuangkeException("未找到当前任务") ;
		}
		
		int nextOrderNum = taskList.get(0).getOrderNum() + 1;
		
		qw = new QueryWrapper<>();
		qw.lambda().eq(FlowRuTask::getOrderId,orderId);
		qw.lambda().eq(FlowRuTask::getStatus,FlowNodeStatusEnum.unstart.getCode());
		qw.lambda().eq(FlowRuTask::getOrderNum,nextOrderNum);
		taskList = flowRuTaskDao.list(qw);
		
		if(CollectionUtils.isEmpty(taskList)) {
			return new ArrayList<>() ;
		}
		
		List<String> userIds = taskList.stream().map(FlowRuTask::getUserId).collect(Collectors.toList()) ;
		
		
		List<Map<String, String>> results = new ArrayList<>() ;
		for(String uId : userIds) {
			SysUser sysUser = sysUserService.findById(uId) ;
			if(sysUser == null) {
				continue ;
			}
			Map<String, String> map = new HashMap<>() ;
			map.put("userId", sysUser.getId());
			map.put("nickName", sysUser.getNickName());
			
			results.add(map) ;
		}
		
		return results;
	}

}
